home *** CD-ROM | disk | FTP | other *** search
- # dnsfind - like find(1), but for DNS data
- #
- # $Id: dnsfind.shar,v 8.2 1996/10/25 17:07:55 vixie Exp $
- # $Source: /proj/src/isc/cvs-1/bind/contrib/misc/dnsfind.shar,v $
- #
- # SYNOPSIS
- # #!/usr/local/bin/perl
- # require 'dnsfind.pl';
- # &dnsfind ('my.domain.com', '1.128.in-addr.arpa');
- # sub dnswanted {
- # printf ("%40s %10s %s\n", $zone, $type, $value);
- # }
- #
- # DESCRIPTION
- # Recursively seaches DNS zones, and calls the user-defined
- # subroutine "dnswanted" for each record found. Fields from the
- # record are put in the following global variables:
- #
- # $parent_zone The parent zone which is being
- # examined via a "dig axfr" (zone
- # transfer) command.
- # $server The canonical name of the primary
- # server for this (parent) zone. From
- # the SOA record.
- # $zone Fully-qualified domain-name.
- # $timeout Seconds before record is out of date.
- # $type Record type (A, MX, CNAME, NS, etc).
- # $value Value of the record.
-
- #
- # Safe return from "require"
- #
- 1;
-
- ##############################################################################
- # dnsfind
- #
- sub dnsfind {
- local (@zones) = @_;
- # GLOBAL ($parent_zone)
- # GLOBAL ($server)
- # GLOBAL ($zone)
- # GLOBAL ($timeout)
- # GLOBAL ($type)
- # GLOBAL ($value)
- # GLOBAL (%_zones_searched)
-
- local ($parent_zone_l);
- local ($server_l);
- local ($zone_l);
- local ($timeout_l);
- local ($type_l);
- local ($value_l);
- local (@sub_zones);
- local (@data);
- local ($record);
- local ($tmp);
-
- foreach $parent_zone_l (@zones) {
- #
- # Find primary
- #
- if (substr ($parent_zone_l, -1) ne '.') {
- $parent_zone_l .= '.';
- }
- @data = &dig ($parent_zone_l, 'soa');
- if (! @data) {
- return undef;
- }
- @data = split (/\s+/, shift (@data));
- $server_l = shift (@data);
-
- #
- # Avoid this zone if we have already searched it
- #
- $tmp = $parent_zone_l;
- $tmp =~ tr/[A-Z]/[a-z]/;
- next if ($_zones_searched{$tmp});
-
- #
- # Transfer zone
- #
- ###print ("[AXFR] $parent_zone_l\n");
- @data = &axfr ($parent_zone_l, $server_l);
- if (! @data) {
- return undef;
- }
-
- #
- # Note this zone as having been searched
- #
- $tmp = $parent_zone_l;
- $tmp =~ tr/[A-Z]/[a-z]/;
- $_zones_searched{$tmp} = 1;
-
- #
- # Process each record
- #
- foreach $record (@data) {
- ($zone_l, $timeout_l, $type_l, $value_l) = split ('\s+', $record,
- 4);
- #
- # We only want data in this zone
- #
- next if (&strcasecmp ($parent_zone_l,
- substr ($zone_l, 0 - length ($parent_zone_l)))
- != 0);
-
- #
- # Call the user-supplied subroutine
- #
- $parent_zone = $parent_zone_l;
- $server = $server_l;
- $zone = $zone_l;
- $timeout = $timeout_l;
- $type = $type_l;
- $value = $value_l;
-
- &dnswanted ();
-
- #
- # Is it a sub-zone, and have we not searched it?
- #
- next if (&strcasecmp ($type_l, 'NS') != 0);
- next if (! &is_sub_zone ($zone_l, $parent_zone_l));
- next if (grep ($_ eq $zone_l, @sub_zones));
- #
- # Save it to search later
- #
- push (@sub_zones, $zone_l);
- }
-
- #
- # Recurse (depth-first) for sub-zones
- #
- &dnsfind (sort (@sub_zones))
- }
- return 1;
- }
-
-
- ##############################################################################
- # is_sub_zone
- #
- sub is_sub_zone {
- local ($zone) = shift (@_);
- local ($parent_zone) = shift (@_);
-
- #
- # To be a sub-zone, you should have a dot, then the parent zone as
- # a suffix.
- #
-
- $parent_zone = '.' . $parent_zone;
-
- if (length ($zone) <= length ($parent_zone)) {
- return 0;
- }
-
- $parent_zone =~ tr/[A-Z]/[a-z]/;
- $zone =~ tr/[A-Z]/[a-z]/;
-
- if (substr ($zone, 0 - length ($parent_zone)) ne $parent_zone) {
- return 0;
- }
-
- return 1;
- }
-
-
- ##############################################################################
- #
- sub axfr {
- local ($zone) = shift (@_);
- local ($server) = shift (@_);
-
- local (@output);
- local (@temp);
- local ($_);
- local ($cont);
-
- @output = split (/\n/, `dig @$server axfr $zone`);
-
- if (! &dig_check ($?, @output)) {
- return ();
- }
-
- #
- # Remove comments and newlines
- #
- @temp = ();
- foreach (@output) {
- s/\s*\n$//;
- s/\s*;.*$//;
- if (! /^\s*$/) {
- push (@temp, $_);
- }
- }
-
- #
- # Collapse continuations
- #
- @output = ();
- while (1) {
- last if (! @temp);
- $_ = shift (@temp);
- while (/\s\($/ && @temp) {
- s/\s*\($//;
- $cont = shift (@temp);
- if ($cont =~ /\)$/) {
- $cont =~ s/\s*\)$//;
- $_ = $_ . ' ' . $cont;
- } else {
- $_ = $_ . ' ' . $cont . ' (';
- }
- }
- push (@output, $_);
- }
-
- return (@output);
- }
-
-
- ##############################################################################
- # dig - Simple DNS lookup, using dig(1).
- #
- sub dig {
- local ($key) = shift (@_);
- local ($type) = shift (@_);
- local ($server) = shift (@_);
-
- local (@output);
- local ($_);
- local (%values);
- local ($value);
- local (@F);
- local (@ret);
- local ($status);
-
- #
- # Key must be relative to root
- #
- if (substr ($key, -1) ne '.') {
- $key .= '.';
- }
-
- if (length ($server)) {
- @output = split (/\n/, `dig @$server $key $type`);
- } else {
- @output = split (/\n/, `dig $key $type`);
- }
-
- if (! &dig_check ($?, @output)) {
- return ();
- }
-
- #
- # Find matching data
- #
- undef %values;
- foreach (@output) {
- next if (/^;/);
- next if (/^\s*$/);
- @F = split;
- next if (&strcasecmp ($type, $F[2]) != 0);
- next if (&strcasecmp ($key, $F[0]) != 0);
- shift (@F); shift (@F); shift (@F);
- $value = join (' ', @F);
- next if ($values{$value});
- $values{$value} = 1;
- push (@ret, $value);
- }
- return @ret;
- }
-
-
- ##############################################################################
- # dig_check - Check how dig(1) went
- #
- sub dig_check {
- local ($status) = shift (@_);
-
- local (@F);
- local ($status_text);
-
- if (! @_) {
- #
- # dig has probably not executed
- #
- $ERROR_STR = "problem running dig?";
- return ();
- }
-
- #
- # Get status text, regardless of return value
- #
- @F = grep (/;; ->>HEADER<<-/, @_);
- if (@F) {
- if ($F[0] =~ /status: ([^,]*),/) {
- $status_text = $1;
- }
- }
- if (($status_text eq "REFUSED") || ($status_text eq "SERVFAIL")) {
- $ERROR_STR = "dig: status: $1";
- return ();
- }
- if ($? != 0) {
- $ERROR_STR = "dig: error, ret = " . ($? >> 8) . ", status = $status";
- return ();
- }
-
- return 1;
- }
-
-
- ##############################################################################
- # strcasecmp - Compare strings while ignoring case
- #
- sub strcasecmp {
- local ($s1) = shift (@_);
- local ($s2) = shift (@_);
-
- $s1 =~ tr/[A-Z]/[a-z]/;
- $s2 =~ tr/[A-Z]/[a-z]/;
-
- return ($s1 cmp $s2);
- }
-